"use strict";

//https://developer.mozilla.org/fr/docs/IndexedDB

app.factory("$indexeddb", ["IndexeddbWrapper", function (IndexeddbWrapper) {
    function IndexeddbService() {
    }
    
    IndexeddbService.prototype = new IndexeddbWrapper("app", 11);
    IndexeddbService.prototype.constructor = IndexeddbService;

    IndexeddbService.prototype.schema = function(db){
        if(db.objectStoreNames.contains("home")) db.deleteObjectStore("home");
        if(db.objectStoreNames.contains("news")) db.deleteObjectStore("news");
        if(db.objectStoreNames.contains("like")) db.deleteObjectStore("like");
        if(db.objectStoreNames.contains("image")) db.deleteObjectStore("image");
        if(db.objectStoreNames.contains("shop")) db.deleteObjectStore("shop");
        if(db.objectStoreNames.contains("account")) db.deleteObjectStore("account");
        if(db.objectStoreNames.contains("subscription")) db.deleteObjectStore("subscription");
        if(db.objectStoreNames.contains("alert")) db.deleteObjectStore("alert");

        db.createObjectStore("home", { keyPath: "_id" });
        db.createObjectStore("news", { keyPath: "_id" });
        db.createObjectStore("like", { keyPath: "_id" });

        var image = db.createObjectStore("image", { keyPath: "src" });
        image.createIndex("request", "dates.request", {unique: false});

        var page = db.createObjectStore("shop", { keyPath: "login" });

        db.createObjectStore("account", { keyPath: "_id" });
        db.createObjectStore("subscription", { keyPath: "_id" });
        db.createObjectStore("alert", { keyPath: "_id" });
    };

    IndexeddbService.prototype.schemaPro = function(db){
        if(db.objectStoreNames.contains("shopAccount")) db.deleteObjectStore("shopAccount");
        if(db.objectStoreNames.contains("shopNews")) db.deleteObjectStore("shopNews");
        if(db.objectStoreNames.contains("shopAlive")) db.deleteObjectStore("shopAlive");
        if(db.objectStoreNames.contains("shopSubscription")) db.deleteObjectStore("shopSubscription");
        
        db.createObjectStore("shopAccount", { keyPath: "_id" });
        db.createObjectStore("shopNews", { keyPath: "_id" });
        db.createObjectStore("shopAlive", { keyPath: "_id" });
        db.createObjectStore("shopSubscription", { keyPath: "_id" });
    };

    IndexeddbService.prototype.clearAll = function(){
        var self = this;
        return self.clear("home").then(function(){
            return self.clear("news");
        }).then(function(){
            return self.clear("like");
        }).then(function(){
            return self.clear("image");
        }).then(function(){
            return self.clear("shop");
        }).then(function(){
            return self.clear("account");
        }).then(function(){
            return self.clear("subscription");
        }).then(function(){
            return self.clear("alert");
        }).then(function(){
            return self.clear("shopAccount");
        }).then(function(){
            return self.clear("shopNews");
        }).then(function(){
            return self.clear("shopAlive");
        }).then(function(){
            return self.clear("shopSubscription");
        });
    };

    return new IndexeddbService();
} ]);

/*-----------------*/

app.factory("IndexeddbWrapper", ["$q", "$timeout", "$window", function ($q, $timeout, $window) {
    function IndexeddbWrapper(name, version) {
        if (!('indexedDB' in $window)) $window.indexedDB = $window.mozIndexedDB || $window.webkitIndexedDB || $window.msIndexedDB;

        this.supported = ('indexedDB' in $window);
        this.name = name;
        this.version = version;
    };

    IndexeddbWrapper.prototype.open = function () {
        var self = this;
        if(!self.supported) {
            return $timeout(function () {
                return $q.reject("indexedDB is not supported.");
            });
        }
        if (!self.db) {
            return self._open(self.name, self.version).then(function (db) { //todo à modifier app, 1 utiliser des constantes angular
                self.db = db;
                return self.db;
            });
        }
        return $timeout(function () {
            return self.db;
        });
    };

    IndexeddbWrapper.prototype._open = function (name, version) {
        if (!name) throw new Error("name is empty.");
        if (!version) throw new Error("version is empty.");
        if (!this.supported) throw new Error("indexeddb is not supported.");
        var self = this;

        var deferred = $q.defer();
        var request = $window.indexedDB.open(name, version);
        request.onerror = function (event) {
            deferred.reject(event);
        };
        request.onsuccess = function (event) {
            var db = new Database(event.target.result);
            deferred.resolve(db);
        };
        request.onupgradeneeded = function (event) {
            if (self.schema) self.schema(event.target.result);
            if (self.schemaPro) self.schemaPro(event.target.result);
        };
        return deferred.promise;
    };

    IndexeddbWrapper.prototype.get = function (name, value) {
        
        if (!name) throw new Error("Name is empty.");
        if (!value) 
            throw new Error("Value is empty.");
        var self = this;

        return self.open().then(function (db) {
            return db.objectStore(name).then(function (o) {
                return o.ninvoke("get", value);
            });
        });
    };

    IndexeddbWrapper.prototype.put = function (name, obj) {
        if (!name) throw new Error("Name is empty.");
        if (!obj) throw new Error("Obj is empty.");
        var self = this;

        return self.open().then(function (db) {
            return db.objectStore(name).then(function (o) {
                return o.ninvoke("put", obj);
            });
        });
    };

    IndexeddbWrapper.prototype.clear = function (name) {
        if (!name) throw new Error("Name is empty.");
        var self = this;

        return self.open().then(function (db) {
            return db.objectStore(name).then(function (o) {
                return o.ninvoke("clear").then(function(){
                    //console.log("clear", name);
                })
            });
        });
    };

    IndexeddbWrapper.prototype.remove = function (name, key) {
        if (!name) throw new Error("Name is empty.");
        if (!key) throw new Error("Obj is empty.");
        var self = this;

        return self.open().then(function (db) {
            return db.objectStore(name).then(function (o) {
                return o.ninvoke("delete", key);
            });
        });
    };

    IndexeddbWrapper.prototype.add = function (name, obj) {
        if (!name) throw new Error("Name is empty.");
        if (!obj) throw new Error("Obj is empty.");
        var self = this;

        return self.open().then(function (db) {
            return db.objectStore(name).then(function (o) {
                return o.ninvoke("add", obj);
            });
        });
    };

    IndexeddbWrapper.prototype.addAll = function (name, array) {
        if (!name) throw new Error("Name is empty.");
        if (!array) throw new Error("Array is empty.");
        var self = this;

        var promises = [];
        array.forEach(function (item) {
            promises.push(self.add(name, item));
        });
        return $q.all(promises);
    };

    IndexeddbWrapper.prototype.getAll = function (name) {
        if (!name) throw new Error("Name is empty.");
        var self = this;

        return self.open().then(function (db) {
            return db.objectStore(name).then(function (o) {
                return o.all();
            });
        });
    };

    IndexeddbWrapper.prototype.first = function (name, keyRange, direction) {
        var self = this;

        return self.open().then(function (db) {
            return db.objectStore(name).then(function (o) {
                return o.first(keyRange, direction);
            });
        });
    };

    IndexeddbWrapper.prototype.index = function (name, field) {
        if (!name) throw new Error("Name is empty.");
        if (!field) throw new Error("Obj is empty.");
        var self = this;

        return self.open().then(function (db) {
            return db.index(name, field);
        });
    };


    /* --------------- */

    function Database(db) {
        this.db = db;
    };

    Database.prototype.createObjectStore = function (name, options) {
        var self = this;
        return $timeout(function () {
            return self.db.createObjectStore(name, options);
        });
    };

    Database.prototype.objectStore = function (name) {
        var self = this;
        return $timeout(function () {
            var o = self.db.transaction([name], "readwrite").objectStore(name);
            return new ObjectStore(o);
        }).catch(function(error){
            console.log("error during objectStore", name);
            throw error;
        });
    };

    Database.prototype.index = function (name, field) {
        var self = this;
        return $timeout(function () {
            var o = self.db.transaction([name], "readwrite").objectStore(name).index(field);
            return new ObjectStore(o);
        });
    };

    /* --------------- */

    function ObjectStore(objectStore) {
        this.objectStore = objectStore;
    };

    ObjectStore.prototype.createIndex = function (name, field, options) {
        var self = this;
        return $timeout(function () {
            return self.objectStore.createIndex(name, field, options);
        });
    };

    ObjectStore.prototype.ninvoke = function (fname /*, args*/) {
        var args = Array.prototype.slice.call(arguments,1);
        var objectStore = this.objectStore[fname];
        var request = objectStore.apply(this.objectStore, args);

        var deferred = $q.defer();
        request.onsuccess = function (event) {
            var data = event.target.result;
            deferred.resolve(data);
        };

        request.onerror = function (event) {
            deferred.reject(event);
        };
        return deferred.promise;
    };

    ObjectStore.prototype.all = function (keyRange, direction) {
        var deferred = $q.defer();

        var request = this.objectStore.openCursor(keyRange, direction);
        var data = [];
        request.onsuccess = function(event) {
            var cursor = event.target.result;
            if (cursor) {
                data.push(cursor.value);
                cursor.continue();
            }
            else deferred.resolve(data);
        };

        request.onerror = function (event) {
            deferred.reject(event);
        };

        return deferred.promise;
    };

    ObjectStore.prototype.first = function (keyRange, direction) {
        var deferred = $q.defer();
        var request = this.objectStore.openCursor(keyRange, direction);
        request.onsuccess = function(event) {
            var cursor = event.target.result;
            if (cursor) deferred.resolve(cursor.value);
            deferred.reject("No cursor");
        };

        request.onerror = function (event) {
            deferred.reject(event);
        };

        return deferred.promise;
    };
    /* --------------- */

    return IndexeddbWrapper;
} ]);